package com.verlet.springbootdemo.simple.nettyrpc.server;

import com.verlet.springbootdemo.simple.nettyrpc.RpcInfo;
import com.verlet.springbootdemo.simple.nettyrpc.RpcResult;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;

import java.lang.reflect.Method;
import java.util.Set;

/**
 * @author verlet
 * @date 2020/4/19
 */
@Slf4j
public class NettyRpcServerHandler extends SimpleChannelInboundHandler<RpcInfo> {


    /**
     * 服务端长时间处理，需要线程处理。
     */
    private static final EventExecutorGroup GROUP = new DefaultEventExecutorGroup(Runtime.getRuntime().availableProcessors() * 2);

    @Override
    public void channelRead0(ChannelHandlerContext ctx, RpcInfo rpcInfo) {
        // 获取调用信息，寻找服务实现类
        GROUP.execute(() -> {
            RpcResult rpcResult = new RpcResult(rpcInfo.getRequestId(), null);
            try {
                String implName = getImplClassName(rpcInfo.getClassName());
                Class<?> clazz = Class.forName(implName);
                Method method = clazz.getMethod(rpcInfo.getMethodName(), rpcInfo.getParamTypes());
                Object result = method.invoke(clazz.getDeclaredConstructor().newInstance(), rpcInfo.getParams());
                rpcResult = new RpcResult(rpcInfo.getRequestId(), result);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                rpcResult.setCause(e);
            } finally {
                ctx.writeAndFlush(rpcResult);
            }
        });
    }

    private String getImplClassName(String interfaceName) throws Exception {
        Class<?> interClass = Class.forName(interfaceName);
        String servicePath = this.getClass().getPackage().getName();
        Reflections reflections = new Reflections(servicePath);
        Set<Class<?>> implClasses = (Set<Class<?>>) reflections.getSubTypesOf(interClass);
        if (implClasses.isEmpty()) {
            log.error("{} impl class is not found!", interfaceName);
            throw new ClassNotFoundException(interfaceName + "impl class is not found!");
        } else if (implClasses.size() > 1) {
            log.error("{} there are many impl classes, not sure invoke which", interfaceName);
            ;
            throw new ClassNotFoundException(interfaceName + "there are many impl classes, not sure invoke which");
        } else {
            Class[] classes = implClasses.toArray(new Class[1]);
            return classes[0].getName();
        }
    }
}
